//
#import "BKFunction.h"
#import "BKFramework.h"
#import "BKSignatures.h"
#import "BKType.h"
#import "BKValue.h"
#import "avcall.h"

@implementation BKFunction

- (id) initWithXMLElement: (NSXMLElement*) elem signatures: (BKSignatures*) sigs
{
	if(self = [super initWithXMLElement: elem signatures: sigs])
	{
		NSXMLElement* e;
		for(e in [elem elementsForName: @"retval"])
		{
			[_retValue release];
			_retValue = [[BKReturnValue alloc] initWithXMLElement: e signatures: sigs];
		}
		if(_retValue == nil)
			_retValue = [[BKReturnValue voidReturnValueWithSignatures: sigs] retain];
		
		NSMutableArray* args = [NSMutableArray array];
		NSInteger i = 0;
		for(e in [elem elementsForName: @"arg"])
		{
			BKArgument* arg = [[[BKArgument alloc] initWithXMLElement: e signatures: sigs] autorelease];
			if(arg)
			{
				[args addObject: arg];
				if(arg.index >= 0)
					i = arg.index + 1;
				else
					arg.index = i++;
			}
		}
		_args = [args retain];
	}
	return self;
}

- (void) dealloc
{
	[_retValue release];
	[_args release];
	[super dealloc];
}

- (void*) function
{
	if(_func == nil)
		_func = CFBundleGetFunctionPointerForName(self.signatures.framework.bundle, (CFStringRef) _name);
	return _func;
}

- (id) callWithArguments: (NSArray*) argValues
{
	void* func = self.function;
	if(func == NULL)
		return nil;
	
	BKType* retType = _retValue.type;
	size_t retSize = [retType byteCount];
	char buffer[256];
	void* retAddr = (retSize <= 256 ? buffer : malloc(retSize));
	
	id result = nil;
	av_alist alist;
	if([_retValue.type startFunction: func returnAddress: retAddr toCall: (struct av_alist*) &alist])
	{
		NSInteger i, count = [_args count];
		if([argValues count] == count)
		{
			for(i = 0; i < count; i++)
			{
				BKType* argType = ((BKArgument*)[_args objectAtIndex: i]).type;
				id argValue = [argValues objectAtIndex: i];
				
				if(![argType addParam: argValue toCall: (struct av_alist*) &alist])
					break;
			}
			
			if(i == count)
			{
				av_call(alist);
				
				result = [NSValue valueWithBytes: retAddr objCType: [_retValue.typeString UTF8String]];
			}
		}
	}
	
	if(retAddr != buffer)
		free(retAddr);
	
	return result;
}

- (NSString*) description
{
	NSMutableString* str = [NSMutableString string];
	[str appendString: [super description]];
	[str appendFormat: @"\n\t%@", _retValue];
	
	id arg;
	for(arg in _args)
		[str appendFormat: @"\n\t%@", arg];
	
	return str;
}

@end
